import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import model.Game;
import model.Hockeyist;
import model.HockeyistState;
import model.HockeyistType;
import model.Move;
import model.Player;
import model.Puck;
import model.Unit;
import model.World;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 * @author Constantine
 */
public class HelpObject {

    public static Unit copy(Unit unit, double x, double y, double speedX, double speedY, double angle, double angularSpeed) {
        return new Unit(unit.getId(), unit.getMass(), unit.getRadius(), x, y, speedX, speedY, angle, angularSpeed) {
        };
    }
    protected final Game game;
    protected final Move move;
    protected final Puck puck;
    protected final double puckX;
    protected final double puckY;
    protected final Hockeyist self;
    protected final double selfX;
    protected final double selfY;
    protected final World world;

    protected static double BACK_AFTER_SHOOT = 0;

    public HelpObject(Hockeyist self, World world, Game game, Move move) {
        this.self = self;
        this.world = world;
        this.game = game;
        this.move = move;
        puck = world.getPuck();
        puckX = Coords.getX(puck.getX());
        puckY = Coords.getY(puck.getY());

        selfX = Coords.getX(self.getX());
        selfY = Coords.getY(self.getY());

        if (BACK_AFTER_SHOOT > 0) {
            BACK_AFTER_SHOOT--;
        }
    }

    public long getNearestTo(Unit unit, List<Unit> hockeyists) {

        double ticks = 0;
        Unit unitCopy = Moving.cast(unit, null, 0.0, 0);
        while (ticks < 200) {
            for (int i = 0; i < hockeyists.size(); i++) {
                if (Moving.atStickSector(hockeyists.get(i), unitCopy)) {
                    return hockeyists.get(i).getId();
                }
                hockeyists.set(i, Moving.cast(hockeyists.get(i), unit, 1.0d, 1));
            }
            unitCopy = Moving.cast(unitCopy, null, 0.0, 1);
            ticks++;
        }
        return getUnits(unit).get(0).getId();
    }

    public List<Unit> getOpps(Unit compare) {
        List<Unit> result = new ArrayList<>();
        for (Unit hockeyist : getUnits(compare)) {
            if (!((Hockeyist) hockeyist).isTeammate()) {
                result.add(hockeyist);
            }
        }
        return result;
    }

    public List<Unit> getTeammates(Unit compare) {
        List<Unit> result = new ArrayList<>();
        for (Unit hockeyist : getUnits(compare)) {
            if (((Hockeyist) hockeyist).isTeammate()) {
                result.add(hockeyist);
            }
        }
        return result;
    }

    public List<Unit> getUnits(Unit compare) {
        List<Unit> result = new ArrayList<>();
        for (Hockeyist hockeyist : world.getHockeyists()) {
            if (hockeyist.getType() != HockeyistType.GOALIE) {
                result.add(hockeyist);
            }
        }
        Collections.sort(result, new ComparatorUnit(compare));
        return result;
    }

    public boolean hasPuck(Unit unit) {
        return world.getPuck().getOwnerHockeyistId() == unit.getId();
    }

    public Hockeyist getHockeyistById(long hockeyistId) {
        for (Hockeyist hockeyist : world.getHockeyists()) {
            if (hockeyist.getId() == hockeyistId) {
                return hockeyist;
            }
        }
        return null;
    }

    public Point getDefenedPoint() {
        return getNet(false);
    }

    public Point getNet(boolean enemyNet) {
        Player player = enemyNet ? world.getOpponentPlayer() : world.getMyPlayer();

        double netX = player.getNetFront();
        double netY = 0.5D * (player.getNetBottom() + player.getNetTop());
        netY += (world.getPuck().getY() < netY ? (0.5D - 0.06451612903225806d) : -(0.5D - 0.06451612903225806d)) * game.getGoalNetHeight();

        return new Point(1, netX, netY);
    }

    public boolean oppBlock(Unit unit, boolean takePuckOwner) {
        for (Unit opp : getOpps(self)) {
            if (((Hockeyist) opp).getState() != HockeyistState.KNOCKED_DOWN && Moving.atStickSector(unit, opp)) {
                if (opp.getId() == world.getPuck().getOwnerHockeyistId() && !takePuckOwner) {
                    continue;
                }
                return true;
            }
        }
        return false;
    }

    public class ComparatorUnit implements Comparator<Unit> {

        private final Unit unit;

        public ComparatorUnit(Unit unit) {
            this.unit = unit;
        }

        @Override
        public int compare(Unit o1, Unit o2) {
            return ((Double) (o1.getDistanceTo(unit) - o2.getDistanceTo(unit))).intValue();
        }

    }

    public List<Unit> getUnitsAhead(Unit unit, List<Unit> units) {
        List<Unit> result = new ArrayList<>();
        for (Unit unitFilter : units) {
            if (Coords.getX(unitFilter.getX()) < Coords.getX(unit.getX())) {
                result.add(unitFilter);
            }
        }
        return result;
    }

    public double getStrikePuckSpeed(double speedStriker, double angleStriker, double speedAngleStriker) {
        return 20 * game.getStrikePowerBaseFactor()
                + speedStriker * Math.cos(angleStriker - speedAngleStriker);
    }

    public double getPassPuckSpeed(double speedStriker, double angleStriker, double passAngle, double speedAngleStrike) {
        return 15 * game.getPassPowerFactor()
                + speedStriker * Math.cos(angleStriker + passAngle - speedAngleStrike);
    }

    public Unit getGoalie() {
        for (Hockeyist hockeyist : world.getHockeyists()) {
            if (!hockeyist.isTeammate() && hockeyist.getType() == HockeyistType.GOALIE) {
                return hockeyist;
            }
        }
        return null;
    }
}
